package org.zjt.config.listener;

import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.zjt.config.operation.UnifiedConfigOperation;
import org.zjt.config.event.ConfigModifyEvent;
import org.zjt.config.properties.ApplicationProperties;
import org.zjt.config.properties.ExtensionProperties;
import org.zjt.config.properties.RemotePropertiesHolder;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.zjt.config.register.Register;
import org.zjt.config.util.SPIExtensionLoader;
import java.lang.reflect.Field;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

import static org.zjt.config.common.Constants.FIELD_SEPARATOR;

/**
 * @author <a href="juntao.zhang@bkjk.com">juntao.zhang</a>
 * @Description:
 * @Package com.example.config.listener
 * @date 2017/12/13 19:40
 * @see
 */
public class RemoteConfigListener implements ApplicationListener<ApplicationEvent> {


    ConfigurableListableBeanFactory beanFactory ;

    private SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter();

    private CopyOnWriteArraySet<Object> beanSet= new CopyOnWriteArraySet<>();


    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.err.println("RemoteConfigListener : "+event.getClass());
        if (event instanceof ApplicationStartingEvent){
            // 项目启动事件
        }
        else if(event instanceof ApplicationEnvironmentPreparedEvent){
            //加载配置事件
            ConfigurableEnvironment environment = ApplicationEnvironmentPreparedEvent.class.cast(event).getEnvironment();
            ExtensionProperties extensionProperties = buildPropertiesBeanFields(ExtensionProperties.class, environment);
            ApplicationProperties applicationProperties = buildPropertiesBeanFields(ApplicationProperties.class, environment);
            String applicationName = StringUtils.isEmpty(applicationProperties.getName()) ? "application-" + UUID.randomUUID().toString() : applicationProperties.getName();
            applicationProperties.setName(applicationName);

            RemotePropertiesHolder remotePropertiesHolder = RemotePropertiesHolder.getInstance();
            remotePropertiesHolder.setRetry(3);
            beanSet.add(remotePropertiesHolder);

            SPIExtensionLoader<Register> extensionLoader = SPIExtensionLoader.getExtensionLoader(Register.class);
            Register extension = StringUtils.hasText(extensionProperties.getRegister()) ?
                    extensionLoader.getExtension(extensionProperties.getRegister()) :extensionLoader.getAdaptiveExtension();
            UnifiedConfigOperation unifiedConfigOperation = new UnifiedConfigOperation();
            unifiedConfigOperation.setRegister(extension);
            unifiedConfigOperation.setApplicationProperties(applicationProperties);
            beanSet.add(unifiedConfigOperation);
            unifiedConfigOperation.pullConfigInfo();
        }
        else if(event instanceof ApplicationPreparedEvent){
            // 注册bean
            this.beanFactory = ApplicationPreparedEvent.class.cast(event).getApplicationContext().getBeanFactory();
            beanSet.forEach (item -> this.beanFactory.registerSingleton(toLowerCaseFirstOne(item.getClass().getName()), item));
        }
        else if (event instanceof ConfigModifyEvent) {
            //更新配置事件
            UnifiedConfigOperation unifiedConfigOperation = this.beanFactory.getBean(UnifiedConfigOperation.class);
            unifiedConfigOperation.pullConfigInfo();
        }
    }

    public static String toLowerCaseFirstOne(String s){
        if(Character.isLowerCase(s.charAt(0)))
            return s;
        return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    public <T> T buildPropertiesBeanFields(Class<T> c , Environment environment) {
        ConfigurationProperties annotation = AnnotationUtils.findAnnotation(c, ConfigurationProperties.class);
        try {
            T t = c.newInstance();
            if (annotation != null) {
                String prefix = annotation.prefix();
                Field[] declaredFields = c.getDeclaredFields();
                if (Objects.nonNull(declaredFields)){
                    for (Field item:declaredFields){
                        String name = prefix + FIELD_SEPARATOR + item.getName();
                        String value = environment.getProperty(name);
                        if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(value)){
                            item.setAccessible(true);
                            item.set(t,simpleTypeConverter.convertIfNecessary(value,item.getType()));
                        }
                    }
                }
            }
            return t;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}